#!python
# gpxplist_utils.py

"""
This script is intended to support exploration of the plist data structure
used to support location simulation in the iOS simulator.
As this file format is a alternative to the GPX file format accepted for location
simulation by the Xcode debugger, the format will be referred to in this file as
'gpxplist'.  The motivation for  exploration of this format is that the simulated
location data provided to the simulator when a gpxplist file is being played can
contain data on heading, speed, and other items which cannot be controlled by the
normal GPX format.

Files conforming to this format are found in the directory GPXPLIST_DIR, which is
part of the simulator application install and can only be modified by root.
The initial files in this directory are in 'compressed plist' format, and need
to be converted to human readable XML plists by a command of the form
plutil -convert xml1 %GPXPLIST_DIR%/%name%.plist -o ./%name%.xml.

Examination of the human readable XML shows the following file structure (the examples
here are taken from the 'Freeway Drive.plist' file shipped with the emulator):
+ the file starts with a preamble similar to GPXPLIST_HEADER
+ the file continues with a sequence of items comparable to GPXLIST_ITEM_1
  or GPXLIST_ITEM_2
+ the file ends with a trailer similar to GPXLIST_FOOTER

From the purpose of the file it is relatively easy to infer that each of the
sequence items is likely to reflect a single location data item.  The content
of these sequence items suggests that they may be generated via some binary
to text encoding sequence, and experimentation has shown that they can be
decoded via the python base64.b64decode method into a binary form which
is consistent with that purpose.
"""
GPXPLIST_DIR="/Applications/Xcode.app/Contents/Developer/Platforms"
GPXPLIST_DIR+="/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/System"
GPXPLIST_DIR+="/Library/Frameworks/CoreLocation.framework/Support/SimulationScenarios/"

GPXPLIST_HEADER="""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Locations</key>
	<array>
"""

GPXPLIST_ITEM_1="""
		<data>
		YnBsaXN0MDDUAQIDBAUIKClUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJj
		aGl2ZXLRBgdUcm9vdIABowkKIVUkbnVsbNsLDA0ODxAREhMUFRYXGBkaGxwd
		Hh8gXxAma0NMTG9jYXRpb25Db2RpbmdLZXlDb29yZGluYXRlTGF0aXR1ZGVf
		ECRrQ0xMb2NhdGlvbkNvZGluZ0tleVZlcnRpY2FsQWNjdXJhY3lfEB1rQ0xM
		b2NhdGlvbkNvZGluZ0tleVRpbWVzdGFtcF8QJmtDTExvY2F0aW9uQ29kaW5n
		S2V5SG9yaXpvbnRhbEFjY3VyYWN5XxAca0NMTG9jYXRpb25Db2RpbmdLZXlM
		aWZlc3Bhbl8QJ2tDTExvY2F0aW9uQ29kaW5nS2V5Q29vcmRpbmF0ZUxvbmdp
		dHVkZV8QGmtDTExvY2F0aW9uQ29kaW5nS2V5Q291cnNlXxAca0NMTG9jYXRp
		b25Db2RpbmdLZXlBbHRpdHVkZV8QGWtDTExvY2F0aW9uQ29kaW5nS2V5U3Bl
		ZWRWJGNsYXNzXxAYa0NMTG9jYXRpb25Db2RpbmdLZXlUeXBlI0BCqupIiK/n
		I7/wAAAAAAAAI0Gy071oTZFoI0AUAAAAAAAAI0A+AAAAAAAAI8BeghVAI1ul
		I0BmR64UeuFIIwAAAAAAAAAAIz/6uFHrhR64gAIQAdIiIyQnWCRjbGFzc2Vz
		WiRjbGFzc25hbWWiJSZaQ0xMb2NhdGlvblhOU09iamVjdFpDTExvY2F0aW9u
		EgABhqBfEA9OU0tleWVkQXJjaGl2ZXIACAARABYAHwAoADIANQA6ADwAQABG
		AF0AhgCtAM0A9gEVAT8BXAF7AZcBngG5AcIBywHUAd0B5gHvAfgCAQIKAgwC
		DgITAhwCJwIqAjUCPgJJAk4AAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAA
		AAACYA==
		</data>
"""
GPXPLIST_ITEM_2="""
		<data>
		YnBsaXN0MDDUAQIDBAUIKClUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJj
		aGl2ZXLRBgdUcm9vdIABowkKIVUkbnVsbNsLDA0ODxAREhMUFRYXGBkaGxwd
		Hh8gXxAma0NMTG9jYXRpb25Db2RpbmdLZXlDb29yZGluYXRlTGF0aXR1ZGVf
		ECRrQ0xMb2NhdGlvbkNvZGluZ0tleVZlcnRpY2FsQWNjdXJhY3lfEB1rQ0xM
		b2NhdGlvbkNvZGluZ0tleVRpbWVzdGFtcF8QJmtDTExvY2F0aW9uQ29kaW5n
		S2V5SG9yaXpvbnRhbEFjY3VyYWN5XxAca0NMTG9jYXRpb25Db2RpbmdLZXlM
		aWZlc3Bhbl8QJ2tDTExvY2F0aW9uQ29kaW5nS2V5Q29vcmRpbmF0ZUxvbmdp
		dHVkZV8QGmtDTExvY2F0aW9uQ29kaW5nS2V5Q291cnNlXxAca0NMTG9jYXRp
		b25Db2RpbmdLZXlBbHRpdHVkZV8QGWtDTExvY2F0aW9uQ29kaW5nS2V5U3Bl
		ZWRWJGNsYXNzXxAYa0NMTG9jYXRpb25Db2RpbmdLZXlUeXBlI0BC4bf72lZ4
		I7/wAAAAAAAAI0Gy08YnVgQZI0AUAAAAAAAAI0A+AAAAAAAAI8Bemez74Y1O
		I0Atij1wo9cKIwAAAAAAAAAAI0A5szMzMzMzgAIQAdIiIyQnWCRjbGFzc2Vz
		WiRjbGFzc25hbWWiJSZaQ0xMb2NhdGlvblhOU09iamVjdFpDTExvY2F0aW9u
		EgABhqBfEA9OU0tleWVkQXJjaGl2ZXIACAARABYAHwAoADIANQA6ADwAQABG
		AF0AhgCtAM0A9gEVAT8BXAF7AZcBngG5AcIBywHUAd0B5gHvAfgCAQIKAgwC
		DgITAhwCJwIqAjUCPgJJAk4AAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAA
		AAACYA==
		</data>
"""
GPXPLIST_FOOTER="""
	</array>
	<key>Options</key>
	<dict>
		<key>LocationDeliveryBehavior</key>
		<real>0.0</real>
		<key>LocationRepeatBehavior</key>
		<real>2</real>
	</dict>
</dict>
</plist>

"""


from xml.etree import ElementTree
from binascii import a2b_base64
from base64 import b64decode
import struct

example_xml = GPXPLIST_HEADER + GPXPLIST_ITEM_1 + GPXPLIST_ITEM_2 + GPXPLIST_FOOTER

print "Doing"
root = ElementTree.fromstring(example_xml.strip())
#thePlist = root.find("plist")
theDict = root.find("dict")
theArray = theDict.find("array")
maxVal = []
minVal = []
for element in [theArray.find("data")] :
    node_text = element.text
    node_bytes = bytearray(b64decode(node_text))
    for i in range (0,len(node_bytes)) :
        theByte = node_bytes[i]
        maxVal.append(theByte)
        minVal.append(theByte)
    print type(node_bytes)
    print type(node_bytes[i])
    print type(bytearray(node_bytes)[0])

for element in theArray.findall("data") :
    node_text = element.text
    node_bytes = bytearray(b64decode(node_text))
    for i in range (0,len(node_bytes)) :
        theByte = node_bytes[i]
        if node_bytes[i]>maxVal[i] : maxVal[i] = theByte
        if node_bytes[i]<minVal[i] : minVal[i] = theByte

for i in range(0,len(minVal)) :
    if ( minVal[i] == maxVal[i] ) :
        print "Byte %d is always %d (%c)" % (i,minVal[i],minVal[i])
    else :
        print "Byte %d is always in the range (%d..%d)" % (i,minVal[i],maxVal[i])
for i in range(1,len(minVal)-1) :
    if (
        minVal[i] == 16 and maxVal[i] == 16 and minVal[i+1] == maxVal[i+1] and
        minVal[i-1] == 95
        ) :
        offset = minVal[i+1]
        if True or minVal[i+2+offset]==95 and maxVal[i+2+offset]==95 :
            lo = i+2
            hi = i+2+offset
            substring = minVal[lo:hi]
            ba = bytearray();
            for c in substring : ba.append(c)
            print "(%d .. %d = %s)" % (lo,hi,str(ba))
    elif ( minVal[i] == 35 and maxVal[i] == 35 ) :
        print "(%d .. %d)" % (i+1, i+8)
        print "   %02x %02x %02x %02x %02x %02x %02x %02x" % tuple(minVal[i+1:i+9])
        print "   %02x %02x %02x %02x %02x %02x %02x %02x" % tuple(maxVal[i+1:i+9])
        print "   %9.3f"%(struct.unpack(">d",bytearray("").join(minVal[i+1:i+9])))
print "Done"

